import React, { Component } from 'react';
import './index.css'

const slugify = function (text) {
  return text
    .toString()
    .toLowerCase()
    .replace(/\s+/g, '-') // Replace spaces with -
    .replace(/[^\w\-]+/g, '') // Remove all non-word chars
    .replace(/\-\-+/g, '-') // Replace multiple - with single -
    .replace(/^-+/, '') // Trim - from start of text
    .replace(/-+$/, ''); // Trim - from end of text
};

const data = [
  {
    name: 'Thing 1',
    position: 'left',
    type: 'A',
  },
  {
    name: 'Thing 2',
    position: 'left',
    type: 'B',
  },
  {
    name: 'Thing 3',
    position: 'right',
    type: 'A',
  },
  {
    name: 'Thing 4',
    position: 'right',
    type: 'B',
  },
  {
    name: 'Thing 5',
    position: 'right',
    type: 'B',
  },
  {
    name: 'Thing 6',
    position: 'right',
    type: 'A',
  },
  {
    name: 'Thing 7',
    position: 'right',
    type: 'B',
  },
  {
    name: 'Thing 8',
    position: 'right',
    type: 'A',
  },
  {
    name: 'Thing 9',
    position: 'right',
    type: 'B',
  },
  {
    name: 'Thing 10',
    position: 'right',
    type: 'A',
  },
  {
    name: 'Thing 11',
    position: 'right',
    type: 'B',
  },
  {
    name: 'Thing 12',
    position: 'right',
    type: 'A',
  },
];

class Lines extends Component {
  constructor() {
    super();
    this.svg = null;
    this.container = null;
  }

  componentDidUpdate() {
    // Re-create all the lines. Since we depend on the rendered dom to decide
    // where to draw lines from and too, we CAN'T have react render the lines.
    // React is declarative - components should be a pure function of props
    // and state. But our lines are a function of the RENDERED DOM!
    // So, we have to do it manually...
    // in componentDidUpdate, AFTER react has done it's bit and rendered the dom,
    // we remove all previous lines, and then re-draw new ones. React,
    // and react's virtual DOM, will never know these lines exist.
    if (!this.props.lines || !this.props.container) return null;
    this.svg = findDOMNode(this);
    this.container = findDOMNode(this.props.container);
    while (this.svg.firstChild) {
      this.svg.removeChild(this.svg.firstChild);
    }

    this.props.lines.forEach((line) => {
      this.renderLines(this.svg, line.from, line.to, line.direction);
    });

    window.addEventListener('resize', () => {
      while (this.svg.firstChild) {
        this.svg.removeChild(this.svg.firstChild);
      }
      this.props.lines.forEach((line) => {
        this.renderLines(this.svg, line.from, line.to, line.direction);
      });
    });
  }

  getSVGPosFromScreenPos(x, y) {
    const svg = this.svg;
    let position;
    if (svg.createSVGPoint) {
      let point = svg.createSVGPoint();
      point.x = x;
      point.y = y;
      position = point.matrixTransform(svg.getScreenCTM().inverse());
    } else {
      const svgRect = svg.getBoundingClientRect();
      position = {
        x: x - svgRect.left - svgRect.clientLeft,
        y: y - svgRect.top - svgRect.clientTop,
      };
    }
    return position;
  }

  linearScale(opts) {
    let istart = opts.domain[0],
      istop = opts.domain[1],
      ostart = opts.range[0],
      ostop = opts.range[1];

    return function scale(value) {
      return ostart + (ostop - ostart) * ((value - istart) / (istop - istart));
    };
  }

  renderLines(el, fromRef, toRefs, direction) {
    const fromRect = this.container.querySelector(fromRef).getBoundingClientRect();

    const fromPos =
      direction === 'left'
        ? this.getSVGPosFromScreenPos(fromRect.left, fromRect.top + fromRect.height / 2)
        : this.getSVGPosFromScreenPos(fromRect.right, fromRect.top + fromRect.height / 2);

    const toPositions = toRefs.map((ref) => {
      const toRect = this.container.querySelector(ref).getBoundingClientRect();

      return direction === 'left'
        ? this.getSVGPosFromScreenPos(toRect.right, toRect.top + fromRect.height / 2)
        : this.getSVGPosFromScreenPos(toRect.left, toRect.top + fromRect.height / 2);
    });

    // Get the maximum length between nodes
    const maxLength = toPositions.reduce((prev, next) => {
      const a = Math.abs(next.y - fromPos.y);
      const b = Math.abs(next.x - fromPos.x);
      return Math.max(Math.sqrt(a * a + b * b), prev);
    }, 0);

    let minCurve = 0;
    let maxCurve = 0;

    // Change the curve depending on screen-size
    if (window.matchMedia('(min-width: 600px)').matches) {
      minCurve = 10;
      maxCurve = 30;
    }

    if (window.matchMedia('(min-width: 1000px)').matches) {
      minCurve = 30;
      maxCurve = 100;
    }

    const scale = this.linearScale({
      domain: [maxLength, 0],
      range: [minCurve, maxCurve],
    });

    return toPositions.forEach((toPos, i) => {
      const a = Math.abs(toPos.y - fromPos.y);
      const b = Math.abs(toPos.x - fromPos.x);
      const length = Math.sqrt(a * a + b * b);

      const controlPos1 =
        direction === 'left' ? fromPos.x - scale(length) : fromPos.x + scale(length);

      const path =
        direction === 'left'
          ? `M${fromPos.x} ${fromPos.y} C ${controlPos1} ${fromPos.y}, ${toPos.x + maxCurve} ${
              toPos.y
            }, ${toPos.x} ${toPos.y}`
          : `M${fromPos.x} ${fromPos.y} C ${controlPos1} ${fromPos.y}, ${toPos.x - maxCurve} ${
              toPos.y
            }, ${toPos.x} ${toPos.y}`;

      const newPath = document.createElementNS('http://www.w3.org/2000/svg', 'path'); //Create a path in SVG's namespace
      newPath.setAttribute('d', path);
      newPath.setAttribute('fill', 'none');
      newPath.setAttribute('stroke', 'black');
      newPath.setAttribute('stroke-width', 3);
      newPath.setAttribute('stroke-dasharray', length * 1.2);
      newPath.setAttribute('stroke-dashoffset', length * 1.2);
      newPath.setAttribute('class', 'animatePath');
      el.appendChild(newPath);
    });
  }

  render() {
    return (
      <svg
        xmlns="http://www.w3.org/2000/svg"
        shapeRendering="geometricPrecision"
        className="backgroundSVG"
        id="svg-lines"
      />
    );
  }
}

class MarginStep extends Component {
  constructor() {
    super();
    this.state = {};
  }
  componentDidMount() {
    this.data = data;
  }
  
  // componentWillMount() {
  //   // Some data
  //   this.data = data;
  // }

  getLines() {
    if (!this.state.activeItem) return;

    const invertedDirection = this.state.activeItem.position === 'left' ? 'right' : 'left';
    const relatedItems = this.data
      .filter((d) => {
        return d.position === invertedDirection && d.type === this.state.activeItem.type;
      })
      .map((d) => {
        return `#${slugify(d.name)}`;
      });

    return [
      {
        from: `#${slugify(this.state.activeItem.name)}`,
        direction: invertedDirection,
        to: relatedItems,
      },
    ];
  }

  renderInteractables(position) {
    return this.data
      .filter((d) => {
        return d.position === position;
      })
      .map((interactable) => {
        const activeClass =
          this.state.activeItem && this.state.activeItem.name === interactable.name
            ? 'isActive'
            : '';
        return (
          <div
            id={slugify(interactable.name)}
            className={`interactable ${activeClass}`}
            onClick={(_) => {
              this.setState({ activeItem: interactable });
            }}
          >
            {interactable.name}
          </div>
        );
      });
  }

  render() {
    return (
      <div
        className="stage"
        ref={(d) => {
          this.stage = d;
        }}
      >
        <Lines container={this.stage} lines={this.getLines()} />
        <div className="row">
          <div className="column">{this.renderInteractables('left')}</div>
          <div className="column">{this.renderInteractables('right')}</div>
        </div>
      </div>
    );
  }
}

export default MarginStep;
